﻿package net.wangit.jdbc.executable;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import net.wangit.framework.debug.Recorder;
import net.wangit.jdbc.DBImplementor;
import net.wangit.jdbc.JDBCException;
import net.wangit.jdbc.database.Database;
import net.wangit.jdbc.table.Column;
import net.wangit.jdbc.table.TableMeta;
import net.wangit.jdbc.util.JDBCExceptionReporter;


public class UpdateRow extends AbstractColumnRow implements Executable {

	private static final long serialVersionUID = 1L;

	public UpdateRow() {
	}

	public void execute(DBImplementor db, TableMeta meta, int count[],
			String unitId) throws JDBCException {
		Database database;
		List<Column> primaryKeyColumns;
		List<Column> updateableColumns;
		String sql;
		PreparedStatement ps;
		database = db.getJDBCSettings().getDatabase();
		primaryKeyColumns = meta.getPrimaryKey().getColumns();
		Column versionColumn = meta.getVersionColumn();
		if (versionColumn != null) {
			boolean isDirty = VersionChecker.isDirty(db, meta
					.getQualifiedName(db), primaryKeyColumns, this,
					versionColumn, versionColumnValue);
			if (isDirty)
				throw new DirtyRowException("the data in this row is dirty");
		}
		if (isChanged(primaryKeyColumns)) {
			boolean isDuplicate = ConstraintChecker.isDuplicate(db, meta
					.getQualifiedName(db), primaryKeyColumns, this);
			if (isDuplicate)
				throw new ConstraintException(ConstraintChecker
						.getConstraintExceptionMessage(primaryKeyColumns, this,
								unitId));
		}
		updateableColumns = getUpdateableColumns(meta);
		sql = SQLUtil.toUpdateStatement(meta.getQualifiedName(db),
				updateableColumns, primaryKeyColumns);
		ps = null;
		try {
			ps = db.getBatcher().prepareStatement(sql);
			for (int i = 0; i < updateableColumns.size(); i++) {
				Column column = updateableColumns.get(i);
				String value = getColumnValue(column);
				column.getSqlType(database).set(ps, value, i + 1);
			}

			int skip = updateableColumns.size();
			for (int i = 0; i < primaryKeyColumns.size(); i++) {
				Column column = primaryKeyColumns.get(i);
				String value = getPrimaryKey(column.getName());
				column.getSqlType(database).set(ps, value, skip + i + 1);
			}

			count[1] += ps.executeUpdate();
		} catch (SQLException sqle) {
			JDBCExceptionReporter.logExceptions(sqle);
			throw new JDBCException((new StringBuilder("could not update: "))
					.append(sql).toString(), sqle);
		} finally {
			try {
				db.getBatcher().closeStatement(ps);
			} catch (SQLException sqle) {
				throw new JDBCException("could not close statement", sqle);
			}
		}
		Recorder.countUpdate();
	}

	private List<Column> getUpdateableColumns(TableMeta meta) {
		List<Column> result = new ArrayList<Column>();
		List<Column> columns = meta.getColumns();
		for (int i = 0; i < columns.size(); i++) {
			Column column = columns.get(i);
			if (!column.isAutoByDB()
					&& (get(column.getName()) != null || column
							.isEnabledByGenerator(this)))
				result.add(column);
		}

		return result;
	}

	private boolean isChanged(List<Column> primaryKeyColumns) {
		for (int i = 0; i < primaryKeyColumns.size(); i++) {
			String columnName = primaryKeyColumns.get(i).getName();
			if (!getPrimaryKey(columnName).equals(get(columnName))) {
				return true;
			}
		}

		return false;
	}

	public boolean isInsertRow() {
		return false;
	}

	public boolean isUpdateRow() {
		return true;
	}

	public boolean isDeleteRow() {
		return false;
	}
}
